﻿using System;
using System.Collections.Generic;
using System.Linq;
using EzDesk.Apps.Model.Attendance;

namespace EzDesk.Apps.ServiceImpl.Attendance.Evaluation
{
    public class LateAndEarlyMins
    {
        public int LateMiniutes { get; set; }
        public int EarlyMiniutes { get; set; }
    }

    public abstract class DayAttendanceEvaluatorBase
    {
        protected List<AttendanceInfo> AttendanceInfoList;
        protected DayInfo DayInfo;
        protected AttendanceConfig Config;

        protected bool AllowLate
        {
            get
            {
                var yesterdayAttendanceInfos =
                    AttendanceInfoList.Where(p => p.Date == DayInfo.Date.AddDays(-1)).OrderBy(p => p.ActionTime).ToList();
                var yesterdayLastAttendanceInfo = yesterdayAttendanceInfos.LastOrDefault();
                if (yesterdayLastAttendanceInfo != null)
                {
                    if (yesterdayLastAttendanceInfo.ActionTime >= 
                        Config.DeepOvertimeOn.ToDateTime(yesterdayLastAttendanceInfo.Date))
                    {
                        return true;
                    }
                }
                return false;
            }
        }

        public abstract DayAttendanceEvaluation GetAttendanceEvaluation();

        public virtual int CalculateManhours()
        {
            return getDayManHours(AttendanceInfoList);
        }

        public virtual LateAndEarlyMins CalculateLateAndEarlyMinutes()
        {
            if (!DayInfo.IsNeedToWork)
            {
                return new LateAndEarlyMins()
                {
                    EarlyMiniutes = 0,
                    LateMiniutes = 0,
                };
            }
            var firstOnDutyInfo = FirstOnDutyInfo;
            var lastOffDutyInfo = LastOffDutyInfo;

            var dayWorkingTimeTable = GetWorkingTimeTable(firstOnDutyInfo, lastOffDutyInfo);

            // 计算迟到早退时间
            var lateMiniutes = getLateMiniutes(firstOnDutyInfo, dayWorkingTimeTable);
            var earlyMiniutes = getEarlyMiniutes(lastOffDutyInfo, dayWorkingTimeTable);

            return new LateAndEarlyMins()
            {
                EarlyMiniutes = earlyMiniutes,
                LateMiniutes = lateMiniutes,
            };
        }
        /// <summary>
        /// 判断今日是否加班
        /// </summary>
        /// <returns></returns>
        public virtual bool IsExtraWorkToday()
        {
            // 今天不需要工作，但是今天有工作工时，就是加班
            return !DayInfo.IsNeedToWork && CalculateManhours() > 0;
        }

        /// <summary>
        /// 判断今日是否缺勤
        /// </summary>
        /// <returns></returns>
        public virtual bool IsAbsenseToday()
        {
            return DayInfo.IsNeedToWork && AttendanceInfoList.Count == 0;
        }

        protected virtual AttendanceInfo FirstOnDutyInfo
        {
            get
            {
                var firstOnDutyInfo = AttendanceInfoList
                    .Where(p=>p.Date == DayInfo.Date)
                    .OrderBy(p => p.AttendanceType)
                    .FirstOrDefault(p => p.AttendanceType == AttendanceType.onDuty);
                return firstOnDutyInfo;
            }
        }

        protected virtual AttendanceInfo LastOffDutyInfo
        {
            get
            {
                var lastOffDutyInfo = AttendanceInfoList
                    .Where(p => p.Date == DayInfo.Date)
                    .OrderBy(p => p.AttendanceType)
                    .LastOrDefault(p => p.AttendanceType == AttendanceType.offDuty);
                return lastOffDutyInfo;
            }
        }

        protected List<AttendanceInfo> TodayAttendanceList
        {
            get { return AttendanceInfoList.Where(p => p.Date == DayInfo.Date).OrderBy(p => p.ActionTime).ToList(); }
        }

        protected virtual DayWorkingTimeTable GetWorkingTimeTable(AttendanceInfo firstOnDutyInfo, AttendanceInfo lastOffDutyInfo)
        {
            var stdOndutyTime = Config.StdOnDutyTime.ToDateTime(DayInfo.Date);
            var stdOffdutyTime = Config.StdOffDutyTime.ToDateTime(DayInfo.Date);
            var standardTimerTable = new DayWorkingTimeTable()
                                         {
                                             StdOnDutyTime =
                                                 stdOndutyTime
                                                 .Add(Config.OnDutyOffsetAfterward),
                                             StdOffDutyTime =
                                                 stdOffdutyTime
                                                 .Add(Config.OnDutyOffsetAfterward)
                                         };

            if (firstOnDutyInfo != null)
            {
                if (firstOnDutyInfo.ActionTime >= stdOndutyTime.Add(-Config.OnDutyOffsetForward) &&
                    firstOnDutyInfo.ActionTime <= stdOndutyTime.Add(Config.OnDutyOffsetAfterward))
                {
                    // 在标准上班时间范围内上班
                    var offset = firstOnDutyInfo.ActionTime - Config.StdOnDutyTime.ToDateTime(firstOnDutyInfo.Date);
                    standardTimerTable.StdOnDutyTime = firstOnDutyInfo.ActionTime;
                    standardTimerTable.StdOffDutyTime = stdOffdutyTime.Add(offset);
                }
            }
            if (AllowLate)
            {
                standardTimerTable.StdOnDutyTime = standardTimerTable.StdOnDutyTime.AddMinutes(Config.AllowLateMiniutes);
            }

            return standardTimerTable;
        }





        /// 获得早退时间
        /// </summary>
        /// <param name="normalAttendanceInfo"></param>
        /// <param name="dayWorkingTimeTable"></param>
        /// <returns></returns>
        private int getEarlyMiniutes(AttendanceInfo lastOffDutyInfo, DayWorkingTimeTable dayWorkingTimeTable)
        {
            var earlyMiniutes = 0;

            if (lastOffDutyInfo != null)
            {
                if (lastOffDutyInfo.ActionTime < dayWorkingTimeTable.StdOffDutyTime)
                {
                    earlyMiniutes = (int)
                                    ((dayWorkingTimeTable.StdOffDutyTime - lastOffDutyInfo.ActionTime)
                                        .TotalMinutes);
                }
            }
            return earlyMiniutes;
        }

        /// <summary>
        /// 获得迟到时间
        /// </summary>
        /// <param name="attendanceInfos"></param>
        /// <param name="dayWorkingTimeTable"></param>
        /// <returns></returns>
        private int getLateMiniutes(AttendanceInfo firstOnDutyInfo, DayWorkingTimeTable dayWorkingTimeTable)
        {
            var lateMiniutes = 0;
       
            if (firstOnDutyInfo != null)
            {
                // 考虑迟到延迟时间
                if (firstOnDutyInfo.ActionTime > dayWorkingTimeTable.StdOnDutyTime.Add(Config.StdOnDutyTimeDelay))
                {
                    lateMiniutes = (int)
                                   ((firstOnDutyInfo.ActionTime - dayWorkingTimeTable.StdOnDutyTime)
                                       .TotalMinutes);
                }
            }
            return lateMiniutes;
        }

        /// <summary>
        /// 当日获得的工时
        /// </summary>
        /// <returns></returns>
        private int getDayManHours(List<AttendanceInfo> attendanceInfos)
        {
            //todo: 根据头一天和后一天的数据进行计算
            var todayAttendanceInfos = TodayAttendanceList;
            var lastDayAttendanceInfos =
                attendanceInfos.Where(p => p.Date == DayInfo.Date.AddDays(-1)).OrderBy(p => p.ActionTime).ToList();
            var nextDayAttendanceInfos =
                attendanceInfos.Where(p => p.Date == DayInfo.Date.AddDays(1)).OrderBy(p => p.ActionTime).ToList();

            var expectedType = AttendanceType.onDuty;
            AttendanceInfo currentOnDutyInfo = null;
            var totalManhours = TimeSpan.Zero;

            // 如果最后一次考勤是上班，那么找找后一天的考勤记录，看看能不能匹配上
            if (todayAttendanceInfos.LastOrDefault() != null && nextDayAttendanceInfos.FirstOrDefault() != null &&
                todayAttendanceInfos.Last().AttendanceType == AttendanceType.onDuty &&
                nextDayAttendanceInfos.First().AttendanceType == AttendanceType.offDuty)
            {
                totalManhours += (nextDayAttendanceInfos.First().ActionTime - todayAttendanceInfos.Last().ActionTime);
            }

            todayAttendanceInfos.ForEach(
                attendanceInfo =>
                    {
                        if (expectedType == AttendanceType.onDuty &&
                            attendanceInfo.AttendanceType == AttendanceType.onDuty)
                        {
                            // 找到了上班
                            expectedType = AttendanceType.offDuty;
                            currentOnDutyInfo = attendanceInfo;
                        }
                        else if (expectedType == AttendanceType.offDuty &&
                                 attendanceInfo.AttendanceType == AttendanceType.offDuty)
                        {
                            // 找到了下班
                            expectedType = AttendanceType.onDuty;
                            totalManhours += (attendanceInfo.ActionTime - currentOnDutyInfo.ActionTime);

                            DateTime intersectionBegin, intersectionEnd;
                            getTimespanIntersection(
                                Config.LunchBreakStartOn.ToDateTime(attendanceInfo.Date),
                                Config.LunchBreakEndOn.ToDateTime(attendanceInfo.Date),
                                currentOnDutyInfo.ActionTime,
                                attendanceInfo.ActionTime,
                                out intersectionBegin,
                                out intersectionEnd
                                );

                            // 如果这一天是工作日，那么去掉中午吃饭休息时间
                            if (DayInfo.IsNeedToWork)
                            {
                                totalManhours -= intersectionEnd - intersectionBegin;
                            }
                        }
                    }
                );
            var originalManhourMiniutes = (int) totalManhours.TotalMinutes;

            if (AllowLate)
            {
                originalManhourMiniutes += Config.AllowLateMiniutes;
            }

            return originalManhourMiniutes;

        }

        /// <summary>
        /// 如果没有交集，那么intersectionBegin, 与intersectionEnd都会置为DateTime.Min
        /// </summary>
        /// <param name="dateTime"></param>
        /// <param name="dateTime_2"></param>
        /// <param name="dateTime_3"></param>
        /// <param name="dateTime_4"></param>
        /// <param name="intersectionBegin"></param>
        /// <param name="intersectionEnd"></param>
        private void getTimespanIntersection(DateTime dateTimeX1, DateTime dateTimeX2, DateTime dateTimeY1, DateTime dateTimeY2, out DateTime intersectionBegin, out DateTime intersectionEnd)
        {
            intersectionBegin = DateTime.MinValue;
            intersectionEnd = DateTime.MinValue;
            
            if (dateTimeY1 <= dateTimeX2 && dateTimeX1 <= dateTimeY2 )
            {
                intersectionBegin = dateTimeX1;
                intersectionEnd = dateTimeX2;

                if (dateTimeY1 > dateTimeX1)
                {
                    intersectionBegin = dateTimeY1;
                }
                if (dateTimeY2 < dateTimeX2)
                {
                    intersectionEnd = dateTimeY2;
                }
            }
        }

    }
}
